#ifndef mbObj
#include "bsp.h"
#include "stm32f4xx_ll_usart.h"
#include "os_obj.h"
#include "mbapp.h"
#include "com_def.h"
#define mbRxEnable()
#define mbTxEnable()
mb_obj_type mbObj;
#define mbPORT USART3
#define mbURT_IRQn USART3_IRQn
#endif

__STATIC_INLINE void mbswTimerEnable(void);
__STATIC_INLINE void mbswTimerDisable(void);
__STATIC_INLINE void mb_timer_handler(void);
static void mb_cb_set(void);
static void mbObjInit(mb_mode_type mbMode);
static void mbPortTx1Rx0Enable(uint8_t mode);
static void mb_port_Init(mb_mode_type mbMode, uint32_t ulBaudRate, mb_parity_type eParity);
static void mbDataSend(void);
static void mb_port_hal_cfg(void);
static void (*mb_byte_send)(uint8_t dat);

/*****************************************************************************
 * @brief mb_byte_send
 * @param   dat
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/

static void mb_rtu_byte_send(uint8_t dat)
{
	mbPORT->DR = dat;
}

static void mb_asc_byte_send(uint8_t dat)
{
	mbPORT->DR = dat | 0x80;
}

/*****************************************************************************
 * @brief UART interrupt routine.
 * @param   none
 * @return  none
 *****************************************************************************/
void mbISR()
{
	volatile uint32_t IntSt;
	volatile uint8_t Data;

	mbObj.timeout_cnt = 0;

	IntSt = mbPORT->SR;

	if (IntSt & USART_SR_RXNE)
	{
		Data = mbPORT->DR;

		if ((IntSt & (USART_SR_ORE | USART_SR_PE)) != 0)
		{
			if (mbObj.rcv_cnt >= 1)
			{
				mbObj.err_st.bits.hal = 1;
			}
		}

		mb_data_rcv(&mbObj, Data);
	}
	else if ((mbObj.snd_size <= mbObj.snd_cnt) && (IntSt & USART_SR_TC))
	{
		mb_send_end(&mbObj);
		mbPortTx1Rx0Enable(0);
	}
	else if (IntSt & USART_SR_TXE)
	{
		if (mb_data_send(&mbObj, mb_byte_send, 1))
		{
			mbPORT->CR1 &= ~USART_CR1_TXEIE;
			mbPORT->CR1 |= USART_CR1_TCIE;
		}
	}
	else
	{
	}
}

/*****************************************************************************
 * @brief   	com modle init .
 * @param   none
 * @return  none
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void mb_port_Init(mb_mode_type mbMode, uint32_t ulBaudRate, mb_parity_type eParity)
{
	LL_USART_InitTypeDef cfg = {0};

	mb_port_hal_cfg();

	mbObjInit(mbMode);

	cfg.BaudRate = ulBaudRate;
	cfg.TransferDirection = LL_USART_DIRECTION_TX_RX;
	cfg.HardwareFlowControl = LL_USART_HWCONTROL_NONE;
	cfg.OverSampling = LL_USART_OVERSAMPLING_16;

	mb_byte_send = mb_rtu_byte_send;

	if (mbMode == MB_RTU_MASTER || mbMode == MB_RTU_SLAVE)
	{
		// rtu mode
		if (MB_PAR_NONE == eParity)
		{
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}
		else if (MB_PAR_NONE_1S == eParity)
		{
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}
		else if (MB_PAR_ODD == eParity)
		{
			cfg.Parity = LL_USART_PARITY_ODD;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}
		else
		{
			cfg.Parity = LL_USART_PARITY_EVEN;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}

		mbObj.run_st.bits.rtu_mode = 1;
	}
	else
	{
		// asc mode
		if (MB_PAR_NONE == eParity)
		{
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_2;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}
		else if (MB_PAR_NONE_1S == eParity)
		{
			cfg.Parity = LL_USART_PARITY_NONE;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_8B;
		}
		else if (MB_PAR_ODD == eParity)
		{
			cfg.Parity = LL_USART_PARITY_ODD;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_9B;
		}
		else
		{
			cfg.Parity = LL_USART_PARITY_EVEN;
			cfg.StopBits = LL_USART_STOPBITS_1;
			cfg.DataWidth = LL_USART_DATAWIDTH_9B;
		}

		mbObj.run_st.bits.rtu_mode = 0;
	}

	// rtu timer
	if (mbObj.run_st.bits.rtu_mode == 0)
	{
		mbObj.rtu_timer_sv = 240;
		mbObj.timeout_set = 500;
	}
	else
	{
		if (ulBaudRate > 19200)
		{
			mbObj.rtu_timer_sv = 3;
		}
		else
		{
			mbObj.rtu_timer_sv = (com_timer_cal(ulBaudRate, (35 * 11 + 9) / 10, 300, 12000) + 99) / 100;
		}
	}

	LL_USART_Init(mbPORT, &cfg);

	LL_USART_Enable(mbPORT);

	nvic_irq_set(mbURT_IRQn, 0x0F, 1);

	mbPortTx1Rx0Enable(0);
}

/*****************************************************************************
 *
 * @brief   Uart En or Dis.
 *
 * @param   none
 *
 * @return  none
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/

static void mbPortTx1Rx0Enable(uint8_t mode)
{
	volatile uint8_t u8Temp;

	if (0 == mode)
	{
		mbRxEnable();

		mbPORT->CR1 &= ~(USART_CR1_TXEIE | USART_CR1_TCIE);

		mbPORT->CR1 |= USART_CR1_UE | USART_CR1_RE;

		u8Temp = mbPORT->DR;
		while ((mbPORT->SR & (USART_SR_RXNE | USART_SR_ORE)) != 0)
		{
			u8Temp = mbPORT->DR;
			__DSB();
		}

		mbPORT->CR1 |= USART_CR1_RXNEIE;
	}
	else
	{
		mbTxEnable();

		mbPORT->CR1 &= ~(USART_CR1_RE | USART_CR1_RXNEIE | USART_CR1_TCIE);

		mbPORT->CR1 |= USART_CR1_UE | USART_CR1_TE | USART_CR1_TXEIE;
	}
}

/*****************************************************************************
 *
 * @brief   Send data.
 *
 * @param   none
 *
 * @return  none
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void mbDataSend(void)
{
	mbPortTx1Rx0Enable(1);
}

/*****************************************************************************
 *
 * @brief   RTU timer enable.
 *
 * @param   none
 *
 * @return  none
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
__STATIC_INLINE void mbswTimerEnable(void)
{
	mbObj.rtu_timer_cnt = 0;
	mbObj.b_rtu_timer_on = 1;
}

/*****************************************************************************
 *
 * @brief   Timeout handle.
 *          called from ext timer 1 ms
 * @param   none
 *
 * @return  none
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
__STATIC_INLINE void mb_timer_handler(void)
{
	mb_rtu_timer_run(&mbObj);
}

/*****************************************************************************
 *
 * @brief   mb object init.
 *
 * @param   none
 *
 * @return  TURE
 *
 * @ Pass/ Fail criteria: none
 *****************************************************************************/
static void mbObjInit(mb_mode_type mbMode)
{

	mb_obj_init(&mbObj);
	mbObj.adu_send = &mbDataSend;

	mb_cb_set();

	mbObj.master.err_cnt = 0;

	// SLAVE FUNCTION INIT
	if (MB_RTU_MASTER == mbMode || mbMode == MB_ASCII_MASTER)
	{
		mbObj.run_st.bits.master_mode = 1;
	}
	else
	{
		mbObj.run_st.bits.master_mode = 0;
	}
}
